package ex3.render.raytrace;

import java.util.Map;
import math.*;

/**
 * A sphere object, calculated using a center 3D point and a radius
 */
public class Sphere extends Surface {
	protected Point3D center;
	protected double radius;

	public Sphere(Map<String, String> attributes) {
		if (attributes.containsKey("center"))
			center = new Point3D(attributes.get("center"));
		if (attributes.containsKey("radius"))
			radius = Double.valueOf(attributes.get("radius")).doubleValue();
	}

	/**
	 * Sphere-ray intersection, returns the distance to the nearest
	 * intersecion point, or posInf if fails
	 */
	public double nearestIntersection(Ray ray, boolean backFace) {

		// the sphere-ray intersection formula (geometric) as shown in class
		Vec L = new Vec(ray.p, this.center);
		double tM = Vec.dotProd(L, ray.v);

		// we only take tM > 0
		if (tM < 0) {
			return Double.POSITIVE_INFINITY;
		}

		double d2 = Vec.dotProd(L, L) - Math.pow(tM, 2);
		double r2 = Math.pow(radius, 2);

		// d2 > r2, otherwise FALSE
		if (d2 > r2) {
			return Double.POSITIVE_INFINITY;
		}

		double tH = Math.sqrt(r2 - d2);
		// if we have only one point of intersection, return it

		if (tH == 0) {
			return tM;
		}
		double t1 = tM - tH;
		double t2 = tM + tH;

		// if both t's are negative, return neither

		if (t1 <= 0 && t2 <= 0) {
			return Double.POSITIVE_INFINITY;
		}
		// As before, if only one value <= 0, return the positive one (noting
		// which face we are rendering)
		if (t1 <= 0) {
			if (!backFace) {
				return Double.POSITIVE_INFINITY;
			} else
				return t2;
		}

		if (t2 <= 0) {
			if (!backFace) {
				return Double.POSITIVE_INFINITY;
			} else
				return t1;
		}
		// otherwise return the larger of the two if we are on the backface or
		// the min if otherwise
		if (!backFace) {
			if (t1 < t2) {
				return t1;
			} else {
				return t2;
			}
		} else {
			return Math.max(t1, t2);
		}
	}

	/**
	 * The normal vector at a given intersection point
	 */
	public Vec normalAt(Point3D intersection, Ray ray) {
		Vec normal = Point3D.sub(intersection, center);
		normal.normalize();
		return normal;
	}

	/**
	 * Generic constructor to be used in conjunction with init
	 */
	public Sphere() {
	}

	public void init(Map<String, String> attributes) {
		if (attributes.containsKey("center"))
			center = new Point3D(attributes.get("center"));
		if (attributes.containsKey("radius"))
			radius = Double.valueOf(attributes.get("radius")).doubleValue();
	}
}